package Snake;

import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Random;

import Gui.Board;

public class Game {

	
	private Board board;
	private Cell[][] matrizGame;

	//JUMP
	private int NUM_MAX_JUMP= 2;
	private Jump jump;
	private LinkedList<Jump> listJump = new LinkedList<Jump>();
	
	//BARRIER
	private Barrier barrier;
	private int NUM_MAX_BARRIER = 25;
	private LinkedList<Barrier> listBarrier = new LinkedList<Barrier>();


	//FOOD
	private int NUM_MAX_FOOD = 5;
	private Food food;
	private LinkedList<Food> listFood = new LinkedList<Food>();

	//SNAKE
	private int NUM_MAX_SNAKE = 1;
	private int NUM_MAX_TAIL_SNAKE = 5;
	private int NUM_MOVE = 0;


	private LinkedList<Snake> listSnake = new LinkedList<Snake>();
	private static final int leftDirection = 0 ;
	private static final int rightDirection = 1 ;
	private static final int upDirection = 2;
	private static final int downDirection = 3;
	
	private Random gerarNumero = new Random();
	
	private int selected = -1;

	
	/**
	 * Construtor da classes Game
	 * @param board
	 */
	public Game(Board board) {
		this.board = board;
		initGame();
		
//	adjacentCell(new Cell (0,3));
	}
	
	/**
	 * inicializa o jogo
	 */
	private void initGame(){
		createMatrizGame();
		allocateFood();
		allocateBarrier();
		allocateJump();
		allocateSnake();
		initSnake();
	}
	
	/**
	 * 
	 * @return a matriz do jogo
	 */
	public Cell[][] getMatrizGame() {
		return matrizGame;
	}

	/**
	 * adiciona um elemento a matriz do jogo
	 * @param cell
	 */
	public synchronized void addElemenMatriz(Cell cell) {
		matrizGame[cell.getX()][cell.getY()] = cell;
	}
	
	/**
	 * procedimento para cria a matriz do jogo
	 */
	private void createMatrizGame() {
		matrizGame=new Cell[board.getnCol()][board.getnCol()];
		for (int i = 0; i < matrizGame.length; i++) {
			for (int j = 0; j < matrizGame.length; j++) {
				addElemenMatriz(new Cell(i, j));
//				System.out.println(matrizGame[i][j].toString());
			}
		}
	}
	
	/**
	 * procedimento para alocar todas as comidas a matriz
	 */
	private void allocateFood() {
		int i = 0;
		while (i < NUM_MAX_FOOD) {
			int x = ((int) (Math.random() * (board.getnCol())));
			int y = ((int) (Math.random() * (board.getnCol())));
//			System.out.println("é uma Celular Vazia?---> "+ matrizGame[x][y].isEmptyCell(matrizGame[x][y]));
			if (isEmptyCell(matrizGame[x][y])) {
				food = new Food(x, y);
				listFood.add(food);
//				System.out.println("é um Comida? --> "+ matrizGame[x][y].isFood(matrizGame[x][y]));
				addElemenMatriz(food);
//				System.out.println("depois de add é um comida? --> "+ matrizGame[x][y].isFood(matrizGame[x][y]));
				i++;
				
			}
		}
	}

	/**
	 * procedimento para alocar todas as barreiras a matriz 
	 */
	private void allocateBarrier() {
		int i = 0;
		while (i < NUM_MAX_BARRIER ) {
			int x = ((int) (Math.random() * (board.getnCol())));
			int y = ((int) (Math.random() * (board.getnCol())));
//			System.out.println("é uma Celular Vazia?---> "+ matrizGame[x][y].isEmptyCell(matrizGame[x][y]));
			if (isEmptyCell(matrizGame[x][y])) {
				barrier = new Barrier(x, y);
				listBarrier.add(barrier);
//				System.out.println("é um barreira? --> "+ matrizGame[x][y].isBarrier(matrizGame[x][y]));
				addElemenMatriz(barrier);
//				System.out.println("depois de add é um barreira? --> "+ matrizGame[x][y].isBarrier(matrizGame[x][y]));
				i++;
			}
		}
	}

	/**
	 * procedimento para alocar o todos os jump a matriz
	 */
	private void allocateJump() {
		int i = 0;
		while (i < NUM_MAX_JUMP) {
			int x = ((int) (Math.random() * (board.getnCol())));
			int y = ((int) (Math.random() * (board.getnCol())));
//			System.out.println("é uma Celular Vazia?---> "+ matrizGame[x][y].isEmptyCell(matrizGame[x][y]));

			if (isEmptyCell(matrizGame[x][y])) {
				jump = new Jump(x, y);
				listJump.add(jump);
//				System.out.println("é um Jump? --> "+ matrizGame[x][y].isJumpToAxis(matrizGame[x][y]));
				addElemenMatriz(jump);
//				System.out.println("depois de add é um Jump? --> "+ matrizGame[x][y].isJumpToAxis(matrizGame[x][y]));
				i++;
			}
		}
	}


	/**
	 * procedimento para alocar todas as cobras.
	 */
	private void allocateSnake(){
		LinkedList<Cell> sizeSnake = new LinkedList<Cell>();//tamanho da cobra...
		LinkedList<Jump> listOfPossibleJumps = new LinkedList<Jump>();
		int i=0;
		Cell head = null;
		Snake snake = new Snake(head, this, sizeSnake, listOfPossibleJumps);//crio a cobra..
		
		//* ---Fazer um switch para mais de uma cobra... e alocar o mesmo. ----*//
		while(i < 1){//Enquanto a cabeça da cobra não conseguir uma celula disponivel, vai tentando.
			head = new Cell(((int) (Math.random() * (0))), gerarNumero.nextInt(board.getnCol()));
			if(!head.isFood() && !head.isJump() && head.isValidToMove()){				
				System.err.println(head.toString()+" é uma celula vazia ? "+isEmptyCell(head) 
						+ " quantas cobras existe na lista? " + listSnake.size());
				listSnake.add(snake);				
				System.err.println("Já existe cobras na lista? " + listSnake.size());
				sizeSnake.add(head);
				i++;
			}
		}

	}
	
	
	/**
	 * 
	 * @param cell
	 * @return
	 */
	public int jumpToBarrier(Cell cell){
		if(cell.isJump()){
			cell= new Cell(cell.getX(), cell.getY());
			matrizGame[cell.getX()][cell.getY()] = cell;
			return 1;
		}
		return 0;
	}

	
	/**
	 * procedimento alternativo para mover a cobra e comer os objeto Food
	 * @param cellSnake
	 * @param snake
	 * @param listFood
	 */
	public void nMoveAndEat(Cell cellSnake, Snake snake, LinkedList<Food> listFood){//para chegar ao comida
		this.listFood=listFood;
		Food cFood =  listFood.get(((int)Math.random()*listFood.size()));
	

			if(cellSnake.getY() < cFood.getY() ){//DOWN
				
				System.out.println("ESTOU A TENTAR DOWN..." + (cellSnake.getY() < cFood.getY())
						+"\n" + cFood.toString() + "Snake: "+cellSnake.toString());
				cellSnake = matrizGame[cellSnake.getX()][cellSnake.getY() + 1];
				if(cellSnake.isValidToMove() && snake.isSnake(cellSnake)==false){
					if(NUM_MOVE <= NUM_MAX_TAIL_SNAKE){
						snake.getSizeSnake().addFirst(cellSnake);
						NUM_MOVE++;
					}else{
						snake.getSizeSnake().addFirst(cellSnake);
						snake.getSizeSnake().removeLast();
					}
				}else{
					try {
						System.out.println(" wait MOVEeat ");

						wait();
					} catch (InterruptedException e) {
					}
				}
			}
			if(cellSnake.getY() > cFood.getY() ){//UP
				System.out.println("ESTOU A TENTAR UP..." + (cellSnake.getY() > cFood.getY())
						+"\n" + cFood.toString() + "Snake: "+cellSnake.toString());
				cellSnake = matrizGame[cellSnake.getX()][cellSnake.getY() - 1];
				
				if(cellSnake.isValidToMove() && snake.isSnake(cellSnake)==false){
					if(NUM_MOVE <= NUM_MAX_TAIL_SNAKE){
						snake.getSizeSnake().addFirst(cellSnake);
						NUM_MOVE++;
					}else {
						snake.getSizeSnake().addFirst(cellSnake);
						snake.getSizeSnake().removeLast();
					}
				}else{
					try {
						System.out.println(" wait MOVEeat ");

						wait();
					} catch (InterruptedException e) {
						
					}
				}
			}
			if(cellSnake.getX() < cFood.getX() && cellSnake.getY() == cFood.getY() ){//RIGHT
				System.out.println("ESTOU A TENTAR RIGHT..." + (cellSnake.getX() < cFood.getX() && cellSnake.getY() == cFood.getY())
						+"\n" + cFood.toString() + "Snake: "+cellSnake.toString());
				cellSnake = matrizGame[cellSnake.getX()+1][cellSnake.getY()];
				if(cellSnake.isValidToMove() && snake.isSnake(cellSnake)==false){
					if(NUM_MOVE <= NUM_MAX_TAIL_SNAKE){
						snake.getSizeSnake().addFirst(cellSnake);
						NUM_MOVE++;
					}else{
						snake.getSizeSnake().addFirst(cellSnake);
						snake.getSizeSnake().removeLast();
					}
				}else{
					try {
						System.out.println(" wait MOVEeat ");

						wait();
					} catch (InterruptedException e) {
					}
				}
			}
			
			if(cellSnake.getX() > cFood.getX() && cellSnake.getY() == cFood.getY()){//LEFT
				System.out.println("ESTOU A TENTAR LEFT..." + (cellSnake.getX() > cFood.getX() && cellSnake.getY() == cFood.getY())
						+"\n" + cFood.toString() + "Snake: "+cellSnake.toString());
				cellSnake = matrizGame[cellSnake.getX() - 1][cellSnake.getY()];
				if(cellSnake.isValidToMove() && snake.isSnake(cellSnake)==false){
					if(NUM_MOVE <= NUM_MAX_TAIL_SNAKE){
						snake.getSizeSnake().addFirst(cellSnake);
						NUM_MOVE++;
					}else {
						snake.getSizeSnake().addFirst(cellSnake);
						snake.getSizeSnake().removeLast();
					}
				}else{
					try {
						System.out.println(" wait MOVEeat ");

						wait();
					} catch (InterruptedException e) {
					}
				}
			}
			
			if(cellSnake.getX() == cFood.getX() && cellSnake.getY() == cFood.getY()){//EAT
				System.out.println("ESTOU A TENTAR EAT..." + (cellSnake.getX() == cFood.getX() && cellSnake.getY() == cFood.getY())
						+"\n" + cFood.toString() + "Snake: "+cellSnake.toString());
				System.out.println("Acabei de Comer a cell:"+ cFood.toString());
				NUM_MAX_TAIL_SNAKE++;
				for (Food food : listFood) {//remove a comida da lista..
					if(food.getX()==cFood.getX() && food.getY()==cFood.getY())
						listFood.remove(cFood);
				}
				cellSnake = matrizGame[cellSnake.getX()][cellSnake.getY()];
				if(NUM_MOVE <= NUM_MAX_TAIL_SNAKE){
					snake.getSizeSnake().addFirst(cellSnake);
				}else{
					snake.getSizeSnake().addFirst(cellSnake);
					snake.getSizeSnake().removeLast();
				}

			}

		
	}
	/**
	 * @author victorandrade
	 * @param snake
	 * @param listFoods
	 * @throws InterruptedException
	 */
	
	public synchronized void movimento(Snake snake) throws InterruptedException{
//		this.listfood = listFoods;
		Cell headSnake = snake.getSizeSnake().getFirst();//escolho uma adjacente
		Cell adjacenteEscolhida = cellAdjacent(headSnake, snake);//lista adjacent
		
		if(requerCelula(adjacenteEscolhida)){
			System.out.println("Snake head is: "+ headSnake.toString() + " adjacente escolhida é: " + adjacenteEscolhida.toString());
			System.out.println("pode avançar..");
			
			if(adjacenteEscolhida.isFood()){//se for uma comida.. done!
				
				for (Food food : listFood) {
					if (food.equals(adjacenteEscolhida)) {
						NUM_MAX_TAIL_SNAKE++;
						listFood.remove(food);
						Cell nCell= adjacenteEscolhida;
						matrizGame[nCell.getX()][nCell.getY()] = nCell; 
						snake.getSizeSnake().addFirst(nCell);
					}
				}
				System.out.println("passei por uma comida.. " + adjacenteEscolhida.toString() );
			}else if(adjacenteEscolhida.isJump()){//se for uma jump...
				System.out.println(" passei por um jump... " + adjacenteEscolhida.toString());
				NUM_MAX_JUMP--;//Nao faz sentido...
				for (Jump jump : listJump) {
					if(jump.equals(adjacenteEscolhida)){//VERIFICAR...
						snake.getListOfPossibleJumps().add(jump);
						listJump.remove(jump);
						Cell nCell = adjacenteEscolhida;
						matrizGame[nCell.getX()][nCell.getY()] = nCell; 
						System.out.println("quantos santos possiveis tenho? " +snake.getListOfPossibleJumps().size() 
								+ " é a celula "+ snake.getListOfPossibleJumps().toString() );
						snake.getSizeSnake().addFirst(nCell);
					}
				}
				
			}else{
				if(snake.getSizeSnake().size() < NUM_MAX_TAIL_SNAKE){
					snake.getSizeSnake().addFirst(adjacenteEscolhida);
					System.out.println("a crescer!...");
				}else{
					snake.getSizeSnake().addFirst(adjacenteEscolhida);
					snake.getSizeSnake().removeLast();
					System.out.println("done! acabei de movimentar...");

				}
			}
			
		}else{
			System.out.println("vou esperar  estou perante uma barreira?  " + adjacenteEscolhida.isBarrier());
			wait();
		}

	}

	private synchronized boolean requerCelula(Cell adjacenteEscolhida) {
		if(adjacenteEscolhida.isValidToMove())
			return true;
		return false;
		
	}


	
	
	private synchronized Cell cellAdjacent(Cell headSnake, Snake snake) throws InterruptedException {
		
		LinkedList<Cell>adjacentCanMove=new LinkedList<Cell>();;
		Cell adjEscolhida=null;
		for (int i = 0; i < 4; i++) {
			if (leftDirection == i && headSnake.getX() > 0) {
				 Cell leftCell = matrizGame[headSnake.getX() - 1][headSnake.getY()];
				if (listSnake.getFirst().isSnake(leftCell) == false) {
					adjacentCanMove.add(leftCell);// add a lista
//					System.out.println(leftCell.toString());
				} 
			}
			
			if (rightDirection == i && headSnake.getX() < board.getnCol()-1){
				Cell rightCell=matrizGame[headSnake.getX() + 1 ][headSnake.getY()];
				if(listSnake.getFirst().isSnake(rightCell) == false){
					adjacentCanMove.add(rightCell);
//					System.out.println(rightCell.toString());

				}
			}
			
			if (upDirection == i && headSnake.getY() > 0){
				Cell upCell=matrizGame[headSnake.getX()][headSnake.getY() - 1];
				if(listSnake.getFirst().isSnake(upCell) == false ){
					adjacentCanMove.add(upCell);
//					System.out.println(upCell.toString());

				}
			}
			
			if (downDirection == i && headSnake.getY() < board.getnCol()-1){
				Cell downCell=matrizGame[headSnake.getX()][headSnake.getY() + 1];
				if(listSnake.getFirst().isSnake(downCell) == false){
					adjacentCanMove.add(downCell);
//					System.out.println(downCell.toString());
				}
			}
		}
		if(adjacentCanMove.size()>0){
			if(adjacentCanMove.size()==1 && adjacentCanMove.getFirst().isBarrier()){
				Thread.currentThread().interrupt();//acabar o jogo...
			}else{
				int nRandom =  gerarNumero.nextInt(adjacentCanMove.size());
				adjEscolhida =  adjacentCanMove.get(nRandom);
				System.out.println("Temos " +adjacentCanMove.size() + " adjacentes disponiveis e foi escolhido o nRandom "
						+nRandom + " logo, adjcente escolhida foi " + adjEscolhida);
			}

		}else {
			Thread.currentThread().interrupt();//acabar o jogo...
		}
		return adjEscolhida;
	}

	/**
	 * procedimento para inicializar as cobras.
	 */
	public void initSnake(){
		for (int i = 0; i < listSnake.size(); i++) {
			Thread snake = new Thread(listSnake.get(i));
			snake.start();
			System.out.println("foi inicializada a " + listSnake.get(i).getSizeSnake());
		}

	}

	public boolean isEmptyCell(Cell cell) {

		if (cell.isBarrier() || cell.isFood() || cell.isJump()) {
			return false;
		}
		for (Snake s : listSnake) {
			if (s.isSnake(cell))
				return false;
		}

		return true;
	}
	

	public LinkedList<Food> getListfood() {
		return listFood;
	}
		
	public LinkedList<Snake> getListSnake() {
		return listSnake;
	}

	public LinkedList<Jump> getListJump() {
		return listJump;
	}

	public LinkedList<Barrier> getListBarrier() {
		return listBarrier;
	}

	@Override
	public String toString() {
		return "Game [matrizGame=" + Arrays.toString(matrizGame) + "]";
	}

}
